Thread: [C] Editing SubItems in ListView with Win32 API

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Registered User
    Join Date
    Apr 2008
    Posts
    49

    [C] Editing SubItems in ListView with Win32 API

    Good afternoon,

    With the Label Editing property of the Win32 ListView (or List Control), I'm able to edit only the items of a ListView, but not the SubItems.
    My question is: how can I edit SubItems in a ListView, like what I'm able to do with Label Editing?
    I've searched the web (including MSDN) and this forum, but I can only find examples using MFC or .NET.
    (I'm a beginner. I'm learning by myself, but the internet is my only resource.)

    Thank you in advance.

  2. #2
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  3. #3
    Registered User
    Join Date
    Apr 2008
    Posts
    49
    Quote Originally Posted by novacain View Post
    I read the post, but I don't know if I have enough knowledge to write the code on my own based on the instructions you wrote.
    but I will try to write it, and send the doubts as they arise.
    Thank you in advance.

  4. #4
    Registered User
    Join Date
    Apr 2008
    Posts
    49
    Good afternoon,

    I did what you (novacain) told (I didn't know how to do most of it, but I looked at MSDN and figured out), but I got stuck in one step. It is not an error, just something I couldn't figure out how to do.

    First of all, let me show what I've already got (with the code below, I can click in the ListView items/SubItems and have the EditBox created, and destroyed when it looses focus).
    I have a ListView (with LVS_EDITLABELS and LVS_REPORT styles), and created a Proc for it (ListViewProc), which looks for the WM_LBUTTONDOWN messages. Then, it uses SubItemHitTest to see which SubItem was clicked. Then, I use GetSubItemRect to create EditBoxs the same size as the SubItems.
    The code below is the ListViewProc, with further explanations, just in case. The code below works fine.
    Code:
    long _stdcall ListViewProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	switch(message){
    		case WM_LBUTTONDOWN:
    		{
    			/*It uses SubItemHitTest to see which SubItem was clicked,
    			and stores it in the struct itemclicked.*/
    			long x, y;
    			x = (long)LOWORD(lParam);
    			y = (long)HIWORD(lParam);
    			LVHITTESTINFO itemclicked;
    			itemclicked.pt.x = x;
    			itemclicked.pt.y = y;
    			int lResult = ListView_SubItemHitTest(hwnd,&itemclicked);
    			/*If SubItemHitTest doesn't return any error (lResult!=-1), it gets the Rect
    			of the SubItem (or Item) clicked, and creates an EditBox (hEdit) with the
    			same size as the SubItem. Then, it sets focus on hEdit and sets a
    			callback function (EditProc) for it.*/
    			if (lResult!=-1){
    				RECT subitemrect;
    				ListView_GetSubItemRect(hwnd,itemclicked.iItem,itemclicked.iSubItem,LVIR_BOUNDS,&subitemrect);
    				int altura = subitemrect.bottom - subitemrect.top;
    				int largura = subitemrect.right - subitemrect.left;
    				if (itemclicked.iSubItem==0){largura=largura/4;}; /*NOTE: the ListView has 4 columns;
    										when iSubItem == 0 (an item is clicked),
    										the width (largura) is divided by 4,
    										because for items (not subitems) the
    										width returned is that of the whole row.*/
    				hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD|WS_VISIBLE|ES_WANTRETURN, 
    				subitemrect.left, subitemrect.top, largura, 1.5*altura, hwnd, 0, GetModuleHandle(NULL), NULL);
    				if(hEdit == NULL){MessageBox(hwnd, "Could not create edit box.", "Error", MB_OK | MB_ICONERROR);};
    				SetFocus(hEdit);
    				EOldProc = (WNDPROC)SetWindowLong(hEdit, GWL_WNDPROC, (LONG)EditProc);
    			} else {
    				/*If SubItemHitTest does return error (lResult=-1),
    				it kills focus of hEdit in order to destroy it.*/
    				SendMessage(hEdit,WM_KILLFOCUS,0,0);
    			}
    			return 0;
    			break;
    		}
    	}
    	/*Other messages are sent to the original Proc
    	(LVOldProc), which is defined globally.*/
    	return CallWindowProc(LVOldProc, hwnd, message, wParam, lParam);
    }
    Then, there is the EditProc callback function, which hEdit uses to intercept WM_KILLFOCUS messages. The code is below.

    Code:
    long _stdcall EditProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	switch(message){
    		case WM_KILLFOCUS:
    		{
    			//Here, it needs to send the ListView a LVN_ENDLABELEDIT notification.
    			DestroyWindow(hwnd);
    			break;
    		}
    	}
    
    	return CallWindowProc(EOldProc, hwnd, message, wParam, lParam);
    }
    The problem is that, when hEdit looses focus, it needs to send the ListView a LVN_ENDLABELEDIT notification before it's destroyed (via DestroyWindow). I know that I have to set a LV_DISPINFO struct, but I have no idea how to start (I've looked at MSDN, but it got me confused).
    I'm also not sure how the ListView will handle the notification (I know it's through WM_NOTIFY) and change the SubItem's text.
    If you (or anyone) could help, I would appreciate it.

    Thank you in advance for your patience.

  5. #5
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Code:
    LV_DISPINFO lvDispinfo;
    ZeroMemory(&lvDispinfo,sizeof(LV_DISPINFO));
    lvDispinfo.hdr.hwndFrom = hWnd;
    lvDispinfo.hdr.idFrom = IDC_LV_EDIT;//the edits ID
    lvDispinfo.hdr.code = LVN_ENDLABELEDIT;
    lvDispinfo.item.mask = LVIF_TEXT | LVIF_PARAM;	
    lvDispinfo.item.iItem = iItem;//row
    lvDispinfo.item.iSubItem = iSubItem;//col
    
    //if user did not enter any text or cancelled
    lvDispinfo.item.pszText =NULL;
    
    //else provide pointer to string 
    lvDispinfo.item.pszText = szEditText;
    lvDispinfo.item.cchTextMax = lstrlen(szEditText);
    
    //pass additional info as the lParam
    lvDispinfo.item.lParam = dwSomeOtherData;
    
    //send the WM_NOTIFY to the LVs parent  window
    SendMessage(hWnd_LV_Parent, WM_NOTIFY, (WPARAM)IDC_LV, (LPARAM)&lvDispinfo); //the LV ID and the LVs Parent window's HWND
    
    //close the edit (will call DestroyWindow())
    PostMessage(WM_CLOSE);
    In the LVs parent callback handle the WM_NOTIFY.
    Check for the LVs ID and LVN_ENDLABELEDIT msg.
    When you receive one, send the lParam (the LV_DISPINFO) to the LV using a LVN_ENDLABELEDIT msg.

    In the LVs callback handle the LVN_ENDLABELEDIT msg.
    If the pszText member is not NULL, set the text in the LV using the LV_ITEM data.
    Last edited by novacain; 12-19-2009 at 03:59 AM.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  6. #6
    Registered User
    Join Date
    Apr 2008
    Posts
    49
    Thank you.

    Just one more thing. Here's the WM_KILLFOCUS of the EditBox (I get the text and store it in szEditText right before the editbox gets destroyed... it works, but see if it looks right to you):
    Code:
    		case WM_KILLFOCUS:
    		{
    			LV_DISPINFO lvDispinfo;
    			ZeroMemory(&lvDispinfo,sizeof(LV_DISPINFO));
    			lvDispinfo.hdr.hwndFrom = hwnd;
    			lvDispinfo.hdr.idFrom = GetDlgCtrlID(hwnd);
    			lvDispinfo.hdr.code = LVN_ENDLABELEDIT;
    			lvDispinfo.item.mask = LVIF_TEXT | LVIF_PARAM;	
    			lvDispinfo.item.iItem = itemclicked.iItem;//row
    			lvDispinfo.item.iSubItem = itemclicked.iSubItem;//col
    			lvDispinfo.item.pszText = NULL;
    			char szEditText[10];
    			GetWindowText(hwnd,szEditText,10);
    			lvDispinfo.item.pszText = szEditText;
    			lvDispinfo.item.cchTextMax = lstrlen(szEditText);
    			SendMessage(GetParent(GetDlgItem(b,MATRIZ)),WM_NOTIFY,(WPARAM)MATRIZ,(LPARAM)&lvDispinfo);
    			DestroyWindow(hwnd);
    			break;
    		}
    The problem is where I should get iItem and iSubItem. In the code above, I'm getting it from the struct itemclicked (now defined globally), which, as you can see in a previous code, stores data from the hittesting of the ListView. But it doesn't get the right information, because, as soon as hEdit looses focus (that is, when I click in another place/item of the ListView), itemclicked receives by hittest another iItem and iSubItem information from the new place/item in which I've clicked. So, everytime hEdit looses focus, it also looses the correct informations about Item and SubItem.
    So, how should I retrieve Item and SubItem information?

    Thank you in advance.
    Last edited by pc2-brazil; 12-19-2009 at 11:07 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Win32 API or Win32 SDK?
    By jverkoey in forum A Brief History of Cprogramming.com
    Replies: 2
    Last Post: 07-20-2005, 03:26 PM
  2. OpenSSL and Win32 SSL API :: SSL/TLS
    By kuphryn in forum Networking/Device Communication
    Replies: 0
    Last Post: 03-10-2004, 07:46 PM
  3. FILES in WinAPI
    By Garfield in forum Windows Programming
    Replies: 46
    Last Post: 10-02-2003, 06:51 PM
  4. OLE Clipboard :: Win32 API vs. MFC
    By kuphryn in forum Windows Programming
    Replies: 3
    Last Post: 08-11-2002, 05:57 PM
  5. Thread Synchronization :: Win32 API vs. MFC
    By kuphryn in forum Windows Programming
    Replies: 2
    Last Post: 08-09-2002, 09:09 AM